package com.beeasy.hzbpm.service;

import cn.hutool.core.util.StrUtil;
import com.beeasy.hzbpm.bean.JsEngine;
import com.beeasy.hzbpm.bean.Notice;
import com.beeasy.hzbpm.entity.BpmInstance;
import com.beeasy.hzbpm.entity.BpmTrigger;
import com.github.llyb120.nami.core.Async;
import com.github.llyb120.json.Arr;
import com.github.llyb120.json.Json;
import com.github.llyb120.json.Obj;
import org.bson.Document;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.beeasy.hzbpm.bean.MongoService.db;
import static com.beeasy.hzbpm.service.WorkService.work;
import static com.github.llyb120.json.Json.*;

public class TriggerService {
    private Document workflow;
    private BpmService bpmService;
//    private List<Document> form;
    private Obj form = o();

    private TriggerService(BpmService bpmService){
        this.workflow = db.getCollection("workflow").find(o("_id", bpmService.ins.bpmId).toBson()).first();
        List<Document> form = (List<Document>) db.getCollection("form")
            .aggregate(
                a(
                    o("$match", o("_id", this.workflow.getObjectId("formId"))),
                    o("$project", o("form", "$form.data"))
                ).toBson()
            ).first()
            .get("form");
        for (Document document : form) {
            this.form.put(document.getString("title"), document);
        }

        this.bpmService = bpmService;
    }


    public static void trigger(BpmService service, String event, BpmInstance.CurrentNode cNode){
        Async.execute(() -> {
            try{
                new TriggerService(service).trigger(event, cNode);
            }catch (Exception e){
                e.printStackTrace();
            }
        });
    }

    public void trigger(String event, BpmInstance.CurrentNode cNode){
        org.bson.Document document = db.getCollection("workflow").find(o("_id", bpmService.ins.bpmId).toBson()).first();
        if (document == null) {
            return;
        }
        //@mayerror
        List map = ro(document).get("trigger.list", List.class);//Json.get(document, ("trigger.list"));
        if (map == null) {
            return;
        }
        List<String> allFields;
        if (cNode != null) {
            allFields = bpmService.getNodeAllFields(cNode.nodeId);
        } else {
            allFields = new ArrayList<>();
        }
        for (Object o : map) {
            BpmTrigger trigger = Json.cast(o, BpmTrigger.class);
            if(!trigger.event.equals(event)){
                continue;
            }
            //保存
//            if(event.equals("EVENT_SAVE") || event.equals("EVENT_GO_NEXT")){
            //检查属性变化
            List<String> changed = bpmService.ins.oldAttrs().entrySet().stream()
                .filter(e -> !Objects.equals(e.getValue(), bpmService.ins.attrs.get(e.getKey())))
                .map(e -> e.getKey())
                .collect(Collectors.toList());
            if(checkTriggerCondition( trigger.condition, changed, allFields)){
                triggerDoAction(trigger, cNode);
            }
//            }
        }
    }

    private boolean checkTriggerCondition(List<BpmTrigger.TriggerCondition> conditions, List<String> changed, List<String> allFields){
        for (BpmTrigger.TriggerCondition condition : conditions) {
            if(condition.field.startsWith("[") && condition.field.endsWith("]")){
                String real = condition.field.substring(1, condition.field.length() - 1);
                //只要有一个符合就命中
                if(real.equals("被改变的字段")){
                    if(changed.stream().anyMatch(e -> triggerCalculateExpression(real, e, condition.op, condition.value))){
                        return true;
                    }
                } else if(real.equals("被提交的字段")){
                    if(
                        allFields.stream()
                            .anyMatch(e -> triggerCalculateExpression(real, e, condition.op, condition.value))
                    ){
                        return true;
                    }
                }
            } else {
                if(!triggerCalculateExpression(condition.field, bpmService.ins.attrs.s(condition.field), condition.op, condition.value)){
                    return false;
                }
            }
        }
        return true;
    }


    private void triggerDoAction(BpmTrigger trigger, BpmInstance.CurrentNode cNode){
        for (BpmTrigger.TriggerAction action : trigger.action) {
            try{
                if(action.type.equals("ACTION_SEND_NOTICE")){
                    triggerActionSendNotice(action, cNode);
                } else if(action.type.equals("ACTION_START_BPM")){
                    triggerActionStartBpm(action, cNode);
                }
            } catch (Exception e){}
        }
    }



    private List<String> getTriggerReceivers(BpmTrigger.TriggerAction action, BpmInstance.CurrentNode cNode){
        String to = action.args.s("to", "");
        Arr receivers = a();
        if("PUBLISHER".equals(to)){
            receivers.add(bpmService.ins.pubUid);
        } else if("CURRENT_DEALERS".equals(to)){
            if (cNode == null) {
                //拉出所有当前节点
                for (BpmInstance.CurrentNode currentNode : bpmService.ins.currentNodes) {
                    receivers.add(currentNode.mainUsers.entrySet());
                    receivers.add(currentNode.supportUsers.entrySet());
                }
            } else{
                receivers.add(cNode.mainUsers.entrySet());
                receivers.add(cNode.supportUsers.entrySet());
            }
        } else if("ALL_DEALERS".equals(to)){
            // 全部经办人
            List<BpmInstance.DataLog> logs = bpmService.ins.logs;
            Set<String> uidList = logs.stream()
                .map(e -> e.uid)
                .filter(e -> e != null)
                .collect(Collectors.toSet());
            receivers.addAll(uidList);
        } else if("CAN_COMMENT".equals(to)){
            Document ps = (Document) workflow.get("permissionSet");
            List oids = a();
            for (Map.Entry<String, Object> entry : ps.entrySet()) {
                List<String> list = (List<String>) entry.getValue();
                if(list.contains("comment")){
                    oids.add(entry.getKey());
                }
            }
            if(!oids.isEmpty()){
                List uids = work.getRoleUserIds(oids);
//                    sqlManager.execute(new SQLReady(String.format("select uid from t_org_user where oid in (%s)", oids.join(",", "'"))), Obj.class)
//                    .stream()
//                    .map(e -> e.s("uid"))
//                    .collect(Collectors.toList());
                receivers.addAll(uids);
            }
        }
        List rs = (List) receivers.stream().filter(e -> e instanceof String && !StrUtil.equals("-1", (String)e)).distinct().collect(Collectors.toList());
        return rs;
    }

    private void triggerActionStartBpm(BpmTrigger.TriggerAction action, BpmInstance.CurrentNode cNode) {
        String bpmId = action.args.s("bpmId", "");
        if(bpmId.isEmpty()){
            return;
        }
        for (String uid : getTriggerReceivers(action, cNode)) {
            try{
                BpmService service = BpmService.ofModel(bpmId);
                service.createBpmInstance(uid, o());
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    private void triggerActionSendNotice(BpmTrigger.TriggerAction action, BpmInstance.CurrentNode cNode){
        String msg = action.args.s("content", "");
        if(msg.isEmpty()){
            return;
        }
        msg = msg.replaceAll("%", "%%");
        //渲染文本
        Pattern pattern = Pattern.compile("\\[(.+?)\\]");
        Matcher matcher = pattern.matcher(msg);
        StringBuffer sb = new StringBuffer();
        while(matcher.find()){
            String item = matcher.group(1).trim();
            String replace = "";
            if(item.equals("任务ID")){
                replace = bpmService.ins.id;
            } else{
                String value = bpmService.ins.attrs.s(item, "");
                replace = value;
            }
            matcher.appendReplacement(sb, replace);
        }
        matcher.appendTail(sb);
        Notice.sendSystem(getTriggerReceivers(action, cNode), sb.toString(), bpmService.ins._id.toString());
    }



    private boolean triggerCalculateExpression(String field, String left, String op, String right){
        if (left == null) {
            return false;
        }
        if (right == null){
            right = "";
        }
        //取出field明细
        Obj meta = form.oo(field);
        if(meta.s("leipiplugins","").equals("datetime") ){
            switch (meta.s("orgformat")){
                case "日期":
                    left = LocalDateTime.parse(left, DateTimeFormatter.ofPattern("yyyy-MM-dd")).toEpochSecond(ZoneOffset.of("+8")) + "";
                    break;
                case "日期+时间":
                    left = LocalDateTime.parse(left, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).toEpochSecond(ZoneOffset.of("+8")) + "";
                    break;

                case "时间":
                    left = LocalDateTime.of(LocalDate.now(), LocalTime.parse(left, DateTimeFormatter.ofPattern("HH:mm:ss"))).toEpochSecond(ZoneOffset.of("+8")) + "";
                    break;
            }
        } else if(meta.s("leipiplugins", "").equals("macros")){
            switch (meta.s("orgtype")){
                case "当前日期":
                    left = LocalDateTime.parse(left, DateTimeFormatter.ofPattern("yyyy-MM-dd")).toEpochSecond(ZoneOffset.of("+8")) + "";
                    break;
                case "当前日期+时间":
                    left = LocalDateTime.parse(left, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).toEpochSecond(ZoneOffset.of("+8")) + "";
                    break;

                case "当前年份":
                    left = LocalDateTime.parse(left, DateTimeFormatter.ofPattern("yyyy")).toEpochSecond(ZoneOffset.of("+8")) + "";
                    break;

                case "当前时间":
                    left = LocalDateTime.of(LocalDate.now(), LocalTime.parse(left, DateTimeFormatter.ofPattern("HH:mm:ss"))).toEpochSecond(ZoneOffset.of("+8")) + "";
                    break;
            }
        }

        if(Pattern.compile("[\\+\\-\\*\\/\\(\\)]+").matcher(right).find()){
            right = JsEngine.calExpression(o(), right);
        }

        switch (op){
            case "等于":
                return Objects.equals(left,right);
            case "大于":
                return new BigDecimal(left).compareTo(new BigDecimal(right)) > 0;
            case "小于":
                return new BigDecimal(left).compareTo(new BigDecimal(right)) < 0;
            case "大于等于":
                return new BigDecimal(left).compareTo(new BigDecimal(right)) >= 0;
            case "小于等于":
                return new BigDecimal(left).compareTo(new BigDecimal(right)) <= 0;
            case "不等于":
                return !Objects.equals(left,right);
            case "包含":
                return left.contains(right);
            case "不包含":
                return !left.contains(right);
        }
        return false;
    }
}
